﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Text;
using System.Threading.Tasks;
using Windows.Security.Cryptography;
using Windows.Security.Cryptography.Core;
using Windows.Storage.Streams;

namespace SharpDepend.WinRT
{
    internal class AESSecurityManager : IAESSecurityManager
    {
        internal class AESSecurity : IAESSecurity
        {
            private CryptographicKey mKey;
            private IBuffer mInitializeVector;
            private string msInitializeVector;

            static readonly char[] CharacterMatrixForRandomIVStringGeneration = {
			'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 
			'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 
			'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 
			'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 
			'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
		};

            /// <summary>
            /// Reference: http://stackoverflow.com/questions/26904442/winrt-rsa-encrypting-large-data-buffer
            /// </summary>
            public void SetKey(string key, int size)
            {
                SymmetricKeyAlgorithmProvider sk = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesCbcPkcs7);
                IBuffer passwordBuffer = CryptographicBuffer.ConvertStringToBinary(key, BinaryStringEncoding.Utf8);
                mKey = sk.CreateSymmetricKey(passwordBuffer);
            }

            public string InitializeVector
            {
                get { return msInitializeVector; }
                set
                {
                    msInitializeVector = value;
                    mInitializeVector = WindowsRuntimeBuffer.Create(UTF8Encoding.UTF8.GetBytes(value), 0, value.Length, value.Length);
                }
            }

            /// <summary>
            /// Reference: http://stackoverflow.com/questions/14582008/what-cryptographically-secure-options-are-there-for-creating-random-numbers-in-w
            /// Reference: http://stackoverflow.com/questions/21488674/readtextasync-in-windows-store-app-with-multibyte-file-and-javascript/21490534#21490534
            /// </summary>
            public void GenerateInitializeVector(int length)
            {
                char[] _iv = new char[length];
                byte[] randomBytes = new byte[length];

                IBuffer randomBuffer = CryptographicBuffer.GenerateRandom((uint)length);
                string buffer = CryptographicBuffer.EncodeToBase64String(randomBuffer);

                using (var reader = DataReader.FromBuffer(randomBuffer))
                {
                    reader.ReadBytes(randomBytes);
                }

                for (int i = 0; i < _iv.Length; i++)
                {
                    int ptr = randomBytes[i] % CharacterMatrixForRandomIVStringGeneration.Length;
                    _iv[i] = CharacterMatrixForRandomIVStringGeneration[ptr];
                }

                InitializeVector = new string(_iv);
            }

            public static string GetHashSha256(string text, int length)
            {
                byte[] bytes = Encoding.UTF8.GetBytes(text);

                // put the string in a buffer, UTF-8 encoded...
                IBuffer input = CryptographicBuffer.ConvertStringToBinary(text, BinaryStringEncoding.Utf8);

                var hasher = HashAlgorithmProvider.OpenAlgorithm("SHA256");
                IBuffer hashed = hasher.HashData(input);

                using (var reader = DataReader.FromBuffer(hashed))
                {
                    byte[] hash = new byte[hashed.Length];
                    reader.ReadBytes(hash);

                    string hashString = string.Empty;
                    foreach (byte x in hash)
                    {
                        hashString += String.Format("{0:x2}", x); //covert to hex string
                    }
                    if (length > hashString.Length)
                        return hashString;
                    else
                        return hashString.Substring(0, length);
                }
            }

            /// <summary>
            /// Reference: http://blog.lordinaire.fr/2012/12/winrt-encryption-and-decryption-with-aes-algorithm/
            /// </summary>
            /// <param name="data"></param>
            /// <returns></returns>
            public byte[] Decrypt(byte[] data)
            {
                IBuffer iData = WindowsRuntimeBuffer.Create(data, 0, data.Length, data.Length);

                IBuffer buffer = CryptographicEngine.Decrypt(mKey, iData, mInitializeVector);
                return buffer.ToArray();
            }

            public byte[] Encrypt(byte[] data)
            {
                IBuffer iData = WindowsRuntimeBuffer.Create(data, 0, data.Length, data.Length);

                IBuffer buffer = CryptographicEngine.Encrypt(mKey, iData, mInitializeVector);
                return buffer.ToArray();
            }
        }

        public IAESSecurity CreateAESSecurity()
        {
            return new AESSecurity();
        }

        public string GetHashSha256(string key, int size)
        {
            return AESSecurity.GetHashSha256(key, size);
        }
    }
}

